#include "mouse_manager.h"
#include <stack>

namespace jlib {


void MouseManager::releaseNode (Node *node) {
  debug_assert(!this->_locked);
  debug_assert(node);
  auto event = &this->_event;
  if (this->_tracker == node) {
    event->setButton(MouseEvent::None);
    node->onMouseCancel(event);
    this->_tracker = nullptr;
  }
  else if (this->_focus == node) {
    node->onMouseBlur(event);
    this->_focus = nullptr;
  }
}



void MouseManager::dispatchMove (Node *root, vec2 location) {
  debug_assert(this->_locked = true);
  auto event = &this->_event;
  event->setLocation(location);
  if (this->_tracker) {
    event->setButton(this->_tracked);
    this->_dispatchDrag(root);
  }
  else
    this->_dispatchMove(root);
  debug_assert(!(this->_locked = false));
}

void MouseManager::dispatchDown (Node *root, int button, vec2 location) {
  debug_assert(this->_locked = true);
  auto event = &this->_event;
  if (!event->isButtonPressed(button)) {
    event->appendStates(button);
    event->setButton(button);
    event->setLocation(location);
    if (this->_tracker)
      this->_tracker->onMouseDown(event);
    else
      this->_dispatchDown(root);
  }
  debug_assert(!(this->_locked = false));
}

void MouseManager::dispatchUp (Node *root, int button, vec2 location) {
  debug_assert(this->_locked = true);
  auto event = &this->_event;
  if (event->isButtonPressed(button)) {
    event->removeStates(button);
    if (this->_tracker) {
      event->setButton(button);
      event->setLocation(location);
      int transfer = this->_tracker->onMouseUp(event);
      if (transfer <= 0) {
        if (transfer == 0)
          this->_focus = this->_tracker;
        this->_tracker = nullptr;
      } else if (event->isButtonPressed(transfer))
        this->_tracked = button;
      else
        this->_tracker = nullptr;
    }
  }
  debug_assert(!(this->_locked = false));
}

void MouseManager::dispatchLeave (Node *root) {
  debug_assert(this->_locked = true);
  auto event = &this->_event;
  event->setButton(MouseEvent::None);
  if (this->_tracker) {
    this->_tracker->onMouseCancel(event);
    this->_tracker = nullptr;
  }
  debug_assert(!(this->_locked = false));
}

void MouseManager::dispatchScroll (Node *root, int scroll, vec2 location) {
  debug_assert(this->_locked = true);
  auto event = &this->_event;
  event->setScroll(scroll);
  event->setLocation(location);
  this->_dispatchScroll(root);
  debug_assert(!(this->_locked = false));
}



bool MouseManager::_dispatchMove (Node *node) {
  auto event = &this->_event;
  // 询问是否拦截事件 如果被拦截 不再向子节点派发
  if (node->isMouseEnabled() && node->onInterceptMouseMove(event)) {
    // 如果不是当前焦点 释放焦点
    if (this->_focus && this->_focus != node)
      this->_focus->onMouseBlur(event);
    // 如果响应事件 成为当前焦点 否则继续向兄弟节点派发
    if (node->isMouseEnabled() && node->onMouseMove(event)) {
      this->_focus = node;
      return true;
    }
    this->_focus = nullptr;
    return false;
  }
  node->reorderDrawChildren();
  auto children = node->drawQueue();
  auto iter = children.rbegin();
  auto end = children.end();
  // 按渲染顺序递归派发
  for (; iter != end; --iter) {
    auto child = *iter;
    if (child->getDrawOrder() < 0)
      break;
    if (this->_dispatchMove(child))
      return true;
  }
  if (node->isMouseEnabled() && node->onMouseMove(event)) {
    if (this->_focus && this->_focus != node)
      this->_focus->onMouseBlur(event);
    this->_focus = node;
    return true;
  }
  for (; iter != end; --iter) {
    auto child = *iter;
    if (this->_dispatchMove(child))
      return true;
  }
  return false;
}

bool MouseManager::_dispatchDown (Node *node) {
  auto event = &this->_event;
  // 先拦截 如果有捕获 释放当前焦点(包括跟踪节点等于焦点) 并记录跟踪的按钮
  if (node->isMouseEnabled() && node->onInterceptMouseDown(event)) {
    if (node->isMouseEnabled() && node->onMouseDown(event)) {
      if (this->_focus) {
        this->_focus->onMouseBlur(event);
        this->_focus = nullptr;
      }
      this->_tracker = node;
      this->_tracked = event->getButton();
      return true;
    }
    return false;
  }
  node->reorderDrawChildren();
  auto children = node->drawQueue();
  auto iter = children.rbegin();
  auto end = children.end();
  // 按渲染顺序递归派发
  for (; iter != end; --iter) {
    auto child = *iter;
    if (child->getDrawOrder() < 0)
      break;
    if (this->_dispatchDown(child))
      return true;
  }
  if (node->isMouseEnabled() && node->onMouseDown(event)) {
    if (this->_focus) {
      this->_focus->onMouseBlur(event);
      this->_focus = nullptr;
    }
    this->_tracker = node;
    this->_tracked = event->getButton();
    return true;
  }
  for (; iter != end; --iter) {
    auto child = *iter;
    if (this->_dispatchDown(child))
      return true;
  }
  return false;
}

void MouseManager::_dispatchDrag (Node *node) {
  auto event = &this->_event;
  // 如果不是当前跟踪节点 从上向下询问是否拦截 拦截相当于相应Down事件
  if (node != this->_tracker) {
    std::vector<Node*> ancestors;
    for (auto iter = this->_tracker->getParent(); iter != node; iter = iter->getParent())
      ancestors.emplace_back(iter);
    ancestors.emplace_back(node);
    for (auto iter = ancestors.rbegin(), end = ancestors.rend(); iter != end; ++iter) {
      auto parent = *iter;
      if (parent->isMouseEnabled() && parent->onInterceptMouseDrag(event)) {
        this->_tracker->onMouseCancel(event);
        this->_tracker = parent;
        return;
      }
    }
  }
  this->_tracker->onMouseDrag(event);
}

bool MouseManager::_dispatchScroll (Node *node) {
  auto event = &this->_event;
  if (node->isWheelEnabled() && node->onInterceptWheelScroll(event))
    return true;
  node->reorderDrawChildren();
  auto children = node->drawQueue();
  auto iter = children.rbegin();
  auto end = children.end();
  // 按渲染顺序递归派发
  for (; iter != end; --iter) {
    auto child = *iter;
    if (child->getDrawOrder() < 0)
      break;
    if (this->_dispatchScroll(child))
      return true;
  }
  if (node->isWheelEnabled() && node->onWheelScroll(event))
    return true;
  for (; iter != end; --iter) {
    auto child = *iter;
    if (this->_dispatchScroll(child))
      return true;
  }
  return false;
}



} // namespace jlib
